package com.example.demo.encode;

import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Base64;
import java.util.Date;

public class AesCipher {

    /*
     * Number of columns (32-bit words) comprising the State. For this
     * standard, Nb = 4.
     */
    private static final int Nb = 4;
    /*
     * S-box transformation table
     */
    private static final char s_box[] = {
        // 0     1     2     3     4     5     6     7     8     9     a     b     c     d     e     f
        0xdb, 0xfe, 0x68, 0x07, 0xba, 0xa3, 0xd4, 0xcd, 0x0f, 0xf7, 0x6c, 0x81, 0xca, 0x63, 0xf2, 0x97,
        0xa7, 0x46, 0x9d, 0xa4, 0x4d, 0xf1, 0xf5, 0x8a, 0xe4, 0x24, 0xcb, 0x85, 0x95, 0x4b, 0x1e, 0x06,
        0xe2, 0xb2, 0xf0, 0x12, 0xad, 0xbf, 0x3c, 0x96, 0x69, 0x7a, 0xaa, 0x82, 0x01, 0xb6, 0x03, 0x8d,
        0x6e, 0xe6, 0x5e, 0x21, 0xd0, 0x90, 0x73, 0x32, 0xec, 0xeb, 0xdd, 0x2f, 0xa8, 0xb1, 0x9b, 0xda,
        0x56, 0x91, 0x2c, 0x33, 0x54, 0xc7, 0x2a, 0x19, 0x27, 0x41, 0xd9, 0xb4, 0x40, 0x53, 0x64, 0x0b,
        0x28, 0xf8, 0x45, 0x35, 0x88, 0x39, 0x86, 0x6f, 0x99, 0xd7, 0xab, 0xef, 0xc1, 0x51, 0xc0, 0xfb,
        0x6a, 0xee, 0xbc, 0xea, 0x2e, 0x1a, 0x66, 0x98, 0x25, 0x31, 0xe9, 0x47, 0xb0, 0xc5, 0x8b, 0x94,
        0x3b, 0xdf, 0x3d, 0x17, 0x0a, 0x0e, 0xf4, 0x50, 0x71, 0x93, 0xd5, 0x72, 0x9f, 0x34, 0x67, 0xa0,
        0x42, 0x6b, 0xbb, 0x5c, 0xde, 0xa6, 0x78, 0x08, 0x87, 0x52, 0x37, 0x65, 0xc2, 0x60, 0x1c, 0x44,
        0x49, 0xe3, 0x70, 0x58, 0xce, 0x83, 0xfc, 0x18, 0x7d, 0xd2, 0x9c, 0x10, 0xff, 0x7f, 0x02, 0x5f,
        0x0d, 0x5a, 0xc6, 0x9e, 0xf6, 0x75, 0xfd, 0x43, 0xd1, 0xb3, 0x26, 0x15, 0xfa, 0xed, 0x14, 0x55,
        0xaf, 0xc8, 0xb7, 0x3a, 0x22, 0x13, 0xc9, 0x0c, 0x77, 0x59, 0xd6, 0x23, 0x2d, 0x3f, 0xf9, 0x79,
        0x7e, 0x57, 0xcf, 0x6d, 0x5d, 0x62, 0x7c, 0x20, 0x29, 0x5b, 0x3e, 0xbe, 0x38, 0x84, 0xd3, 0xa9,
        0x16, 0x76, 0xa2, 0xa5, 0x48, 0x4f, 0x4c, 0x7b, 0x30, 0x11, 0xb5, 0xcc, 0x80, 0x1b, 0xbd, 0x00,
        0xc3, 0xe7, 0x92, 0x8f, 0x74, 0xb8, 0xc4, 0xae, 0xe8, 0xe0, 0xf3, 0x2b, 0x8c, 0x05, 0x09, 0xb9,
        0xa1, 0x04, 0x89, 0x9a, 0xe5, 0x61, 0x1f, 0xd8, 0xac, 0xe1, 0x1d, 0x4a, 0xdc, 0x36, 0x4e, 0x8e};// f

    /*
     * Inverse S-box transformation table
     */
    private static final char inv_s_box[] = {
        // 0     1     2     3     4     5     6     7     8     9     a     b     c     d     e     f
        0xdf, 0x2c, 0x9e, 0x2e, 0xf1, 0xed, 0x1f, 0x03, 0x87, 0xee, 0x74, 0x4f, 0xb7, 0xa0, 0x75, 0x08,
        0x9b, 0xd9, 0x23, 0xb5, 0xae, 0xab, 0xd0, 0x73, 0x97, 0x47, 0x65, 0xdd, 0x8e, 0xfa, 0x1e, 0xf6,
        0xc7, 0x33, 0xb4, 0xbb, 0x19, 0x68, 0xaa, 0x48, 0x50, 0xc8, 0x46, 0xeb, 0x42, 0xbc, 0x64, 0x3b,
        0xd8, 0x69, 0x37, 0x43, 0x7d, 0x53, 0xfd, 0x8a, 0xcc, 0x55, 0xb3, 0x70, 0x26, 0x72, 0xca, 0xbd,
        0x4c, 0x49, 0x80, 0xa7, 0x8f, 0x52, 0x11, 0x6b, 0xd4, 0x90, 0xfb, 0x1d, 0xd6, 0x14, 0xfe, 0xd5,
        0x77, 0x5d, 0x89, 0x4d, 0x44, 0xaf, 0x40, 0xc1, 0x93, 0xb9, 0xa1, 0xc9, 0x83, 0xc4, 0x32, 0x9f,
        0x8d, 0xf5, 0xc5, 0x0d, 0x4e, 0x8b, 0x66, 0x7e, 0x02, 0x28, 0x60, 0x81, 0x0a, 0xc3, 0x30, 0x57,
        0x92, 0x78, 0x7b, 0x36, 0xe4, 0xa5, 0xd1, 0xb8, 0x86, 0xbf, 0x29, 0xd7, 0xc6, 0x98, 0xc0, 0x9d,
        0xdc, 0x0b, 0x2b, 0x95, 0xcd, 0x1b, 0x56, 0x88, 0x54, 0xf2, 0x17, 0x6e, 0xec, 0x2f, 0xff, 0xe3,
        0x35, 0x41, 0xe2, 0x79, 0x6f, 0x1c, 0x27, 0x0f, 0x67, 0x58, 0xf3, 0x3e, 0x9a, 0x12, 0xa3, 0x7c,
        0x7f, 0xf0, 0xd2, 0x05, 0x13, 0xd3, 0x85, 0x10, 0x3c, 0xcf, 0x2a, 0x5a, 0xf8, 0x24, 0xe7, 0xb0,
        0x6c, 0x3d, 0x21, 0xa9, 0x4b, 0xda, 0x2d, 0xb2, 0xe5, 0xef, 0x04, 0x82, 0x62, 0xde, 0xcb, 0x25,
        0x5e, 0x5c, 0x8c, 0xe0, 0xe6, 0x6d, 0xa2, 0x45, 0xb1, 0xb6, 0x0c, 0x1a, 0xdb, 0x07, 0x94, 0xc2,
        0x34, 0xa8, 0x99, 0xce, 0x06, 0x7a, 0xba, 0x59, 0xf7, 0x4a, 0x3f, 0x00, 0xfc, 0x3a, 0x84, 0x71,
        0xe9, 0xf9, 0x20, 0x91, 0x18, 0xf4, 0x31, 0xe1, 0xe8, 0x6a, 0x63, 0x39, 0x38, 0xad, 0x61, 0x5b,
        0x22, 0x15, 0x0e, 0xea, 0x76, 0x16, 0xa4, 0x09, 0x51, 0xbe, 0xac, 0x5f, 0x96, 0xa6, 0x01, 0x9c};// f

    /* 列变换矩阵  */
    private static final byte colTransMatrix[][] = {
        {6, 1, 3, 4},
        {2, 0, 1, 0},
        {8, 0, 0, 5},
        {5, 2, 5, 5}};

    /* 列逆变换矩阵  */
    private static final byte colInterTransMatrix[][] = {
        {10, -5, -3, -5},
        {65, -35, -20, -32},
        {-20, 11, 6, 10},
        {-16, 8, 5, 8}};

    private static final byte defaultKeyValue[] = {
        (byte) 0xa4, (byte) 0x3e, (byte) 0xd2, (byte) 0xca,
        (byte) 0x71, (byte) 0xce, (byte) 0xd2, (byte) 0x41,
        (byte) 0xa3, (byte) 0xa9, (byte) 0xe8, (byte) 0x33,
        (byte) 0x02, (byte) 0xd9, (byte) 0xdb, (byte) 0xf0,
        (byte) 0xcf, (byte) 0xaf, (byte) 0xde, (byte) 0xc6,
        (byte) 0x6d, (byte) 0x7a, (byte) 0x6a, (byte) 0x07,
        (byte) 0x40, (byte) 0xc5, (byte) 0x45, (byte) 0xf4,
        (byte) 0xb9, (byte) 0xb4, (byte) 0x8f, (byte) 0xb8};

    private static final byte defaultSaltData[] = {
        (byte) 0xa4, (byte) 0x3e, (byte) 0xd2, (byte) 0xca,
        (byte) 0x71, (byte) 0xce, (byte) 0xd2, (byte) 0x41,
        (byte) 0xa3, (byte) 0xa9, (byte) 0xe8, (byte) 0x33,
        (byte) 0x02, (byte) 0xd9, (byte) 0xdb, (byte) 0xf0,
    };

    private static final int ENCODE_DATA_TYPE = 1;
    private AesKeyExpand defaultKeyExpand = null;
    private static final int DEFAULT_KEYVALUE_LENGTH = 32;
    private static final int DEFAULT_SALTVALUE_LENGTH = 16;

    private int handleType;
    private AesCipherUtil aesCipherUtil = null;
    public static final int HANDLE_TYPE_FIXED_DEFAULT_KEY = 0;
    public static final int HANDLE_TYPE_USER_SET_KEY = 1;
    public static final int HANDLE_TYPE_RANDOM_KEY_ENCODE = 2;
    public static final int HANDLE_TYPE_RANDOM_KEY_DECODE = 3;


    /*********************************************************************************
       *Function:  	AesCipher
       *Description： 构造初始化函数
       *Input:  		无
       *Return:  	无
       *Others:
     *CreatBy: 	chendz
     *Modify:
     **********************************************************************************/
    public AesCipher(AesKeyExpand keyExpand) {
        this.handleType = HANDLE_TYPE_USER_SET_KEY;
        initialExpand(keyExpand);
    }

    /*********************************************************************************
       *Function:  	AesCipher
       *Description： 构造初始化函数 使用随机的keyValue 和 salt
       *Input:  		handleType
       *Return:  	无
       *Others:
     *CreatBy: 	chendz
     *Modify:
     **********************************************************************************/
    public AesCipher(int handleType, AesCipherUtil cipher) {
        if (HANDLE_TYPE_RANDOM_KEY_ENCODE != handleType &&
            HANDLE_TYPE_RANDOM_KEY_DECODE != handleType) {
            this.handleType = HANDLE_TYPE_FIXED_DEFAULT_KEY;
            this.defaultKeyExpand = new AesKeyExpand(defaultKeyValue, defaultSaltData);
            initialExpand(this.defaultKeyExpand);
            return;
        }
        this.handleType = handleType;
        this.aesCipherUtil = cipher;

        return;
    }

    /*********************************************************************************
       *Function:  	AesCipher
       *Description： 构造初始化函数 使用默认的keyValue 和 salt
       *Input:  		无
       *Return:  	无
       *Others:
     *CreatBy: 	chendz
     *Modify:
     **********************************************************************************/
    public AesCipher() {
        this.handleType = HANDLE_TYPE_FIXED_DEFAULT_KEY;
        this.defaultKeyExpand = new AesKeyExpand(defaultKeyValue, defaultSaltData);
        initialExpand(this.defaultKeyExpand);
        return;
    }

    /*********************************************************************************
       *Function:  	getKeyOrSaltValueFromBytes
       *Description： 根据输入的字符串的bytes，获取key或者value值。
       *Input:  		byte[] strBytes,   		输入的字符串转换成byte
     int offset 			  	strBytes的取值偏移
     *output:		byte[] keyOrSaltBytes,	 输出的byte值
       *Return:  	无
       *Others:
     *CreatBy: 	chendz
     *Modify:
     **********************************************************************************/
    private void getKeyOrSaltValueFromBytes(byte[] strBytes, byte[] keyOrSaltBytes, int offset) {
        int iCnt = 0;
        int strCnt = 0;
        boolean isStrEnd = false;
        boolean isCntEnd = false;

        if (strBytes.length < offset) {
            offset = offset % strBytes.length;
        }

        for (iCnt = 0, strCnt = offset; (isStrEnd != true) || (isCntEnd != true); iCnt++, strCnt++) {
            if (strCnt >= strBytes.length) {
                isStrEnd = true;
                strCnt = 0;
            }
            if (iCnt >= keyOrSaltBytes.length) {
                isCntEnd = true;
                iCnt = 0;
            } else {
                keyOrSaltBytes[iCnt] = 0;
            }
            keyOrSaltBytes[iCnt] += strBytes[strCnt];
        }

        return;
    }

    /*********************************************************************************
       *Function:  	AesCipher
       *Description： 构造初始化函数 使用默认的keyValue 和 salt
       *Input:  		String keyValue
       *Return:  	无
       *Others:
     *CreatBy: 	chendz
     *Modify:
     **********************************************************************************/
    public AesCipher(String keyValue) {
        this.handleType = HANDLE_TYPE_USER_SET_KEY;
        initialKeyAndSaltByStr(keyValue);
        return;
    }

    /*********************************************************************************
       *Function:  	initialKeyAndSaltByStr
       *Description：  使用String来初始化的keyValue 和 salt
       *Input:  		String keyValue
     *Output:		无
       *Return:  	void
       *Others:
     *CreatBy: 	chendz
     *Modify:
     **********************************************************************************/
    private void initialKeyAndSaltByStr(String keyValue) {
        byte[] strBytes = keyValue.getBytes();
        byte[] keyBytes = new byte[DEFAULT_KEYVALUE_LENGTH];
        byte[] saltBytes = new byte[DEFAULT_SALTVALUE_LENGTH];

        if (0 == strBytes.length) {
            this.defaultKeyExpand = new AesKeyExpand(defaultKeyValue, defaultSaltData);
            initialExpand(this.defaultKeyExpand);
            return;
        }
        /* 获取key */
        getKeyOrSaltValueFromBytes(strBytes, keyBytes, 0);
        /* 获取salt */
        getKeyOrSaltValueFromBytes(strBytes, saltBytes, keyBytes.length);

        this.defaultKeyExpand = new AesKeyExpand(keyBytes, saltBytes);
        initialExpand(this.defaultKeyExpand);
    }

    /*********************************************************************************
       *Function:  	initialExpand
       *Description： 初始化expand
       *Input:  		无
       *Return:  	无
       *Others:
     *CreatBy: 	chendz
     *Modify:
     **********************************************************************************/
    private void initialExpand(AesKeyExpand keyExpand) {

        switch (keyExpand.getKeyValue().length) {
            default:
            case 16:
                keyExpand.setNk(4);
                keyExpand.setNr(10);
                break;
            case 24:
                keyExpand.setNk(6);
                keyExpand.setNr(12);
                break;
            case 32:
                keyExpand.setNk(8);
                keyExpand.setNr(14);
                break;
        }

        keyExpand.setKeyExpand(new byte[Nb * (keyExpand.getNr() + 1) * 4]);
        // 密钥扩展
        key_expansion(keyExpand);
    }

    /*********************************************************************************
       *Function:  	gmult
       *Description：   将a的值乘以b
       *Input:  		IN UINT8 a,
     IN UINT8 b
       *Output:  	UINT8
       *Return:  	VOID
       *Others:  	如果该值的最高位为1（表示该数值不小于128），则还需要将移位后的结果异或00011011(0x1b)
     注意：a和b都是传值调用，返回结果保存在p中（也即a左移1位的结果）
     @param: a=R[0],
     b=0x02(乘以2，也即将该值的二进制位左移1位)
      *CreatBy: 	chendz
      *Modify:
     **********************************************************************************/
    private byte gmult(byte a, byte b) {
        byte p = 0, hbs = 0;
        char i;
        for (i = 0; i < 8; i++) {
            if (0 != (b & 1)) {
                p ^= a;
            }

            hbs = (byte) (a & 0x80);
            a <<= 1;
            if (0 != hbs) {
                a ^= 0x1b;
            }
            b >>= 1;
        }
        return p;
    }

    /*********************************************************************************
       *Function:  	coef_add
       *Description：  数组异或
       *Input:  		IN UINT8* uiDataA,
     IN UINT8* uiDataB
       *Output:  	OUT UINT8* uiOut
       *Return:  	VOID
       *Others:
     *CreatBy: 	chendz
     *Modify:
     **********************************************************************************/
    private void coef_add(byte a[], byte b[], byte d[]) {
        d[0] = (byte) (a[0] ^ b[0]);
        d[1] = (byte) (a[1] ^ b[1]);
        d[2] = (byte) (a[2] ^ b[2]);
        d[3] = (byte) (a[3] ^ b[3]);
    }

    /*********************************************************************************
       *Function:  	Rcon
       *Description：   这里通过Rcon函数计算，所得的值和直接索引RC表一样。
       *Input:  		IN UINT8 i,
     INOUT UINT8* puiRcon
       *Output:  	INOUT UINT8* puiRcon
       *Return:  	UINT8*
       *Others:
     *CreatBy: 	chendz
     *Modify:
     **********************************************************************************/
    private byte[] Rcon(byte i, byte Rcon[]) {

        if (i == 1) {
            Rcon[0] = 0x01; // x^(1-1) = x^0 = 1
        } else if (i > 1) {
            Rcon[0] = 0x02;
            i--;
            while (i - 1 > 0) {
                Rcon[0] = gmult(Rcon[0], (byte) 0x02);
                i--;
            }
        }

        return Rcon;
    }

    /*********************************************************************************
       *Function:  	add_round_key
       *Description：  轮训密钥加密
       *Input:  		byte[] state,
     byte[] w,
     byte r
       *Output:  	byte[] state,
       *Return:  	VOID
       *Others: 		轮密钥加
     state:需要处理的状态数据（4*4矩阵）
     puiKey:轮密钥
     uiRoll:使用第r轮密钥（4*4矩阵为一轮）
     *CreatBy: 	chendz
     *Modify:
     **********************************************************************************/
    private void add_round_key(byte[] state, byte[] w, byte r) {

        byte c;

        for (c = 0; c < Nb; c++) {
            // 按列循环，计算第c列的值
            // state[row,col] = state[row,col]^w[col,row]（r=0）
            state[Nb * 0 + c] = (byte) (state[Nb * 0 + c] ^ w[4 * Nb * r + 4 * c + 0]);   //debug, so it works for Nb
	        // !=4
            state[Nb * 1 + c] = (byte) (state[Nb * 1 + c] ^ w[4 * Nb * r + 4 * c + 1]);
            state[Nb * 2 + c] = (byte) (state[Nb * 2 + c] ^ w[4 * Nb * r + 4 * c + 2]);
            state[Nb * 3 + c] = (byte) (state[Nb * 3 + c] ^ w[4 * Nb * r + 4 * c + 3]);
        }
    }

    /*********************************************************************************
       *Function:  	standardMix_columns
       *Description：  列正变换
       *Input:  		byte[] state
       *Output:  	byte[] state
       *Return:  	VOID
       *Others:
     *CreatBy: 	chendz
     *Modify:
     **********************************************************************************/
    private void standardMix_columns(byte[] state) {
        int i, j, m;
        byte[] tempState;
        byte temp;
        tempState = Arrays.copyOf(state, state.length);

        for (i = 0; i < 4; i++) {
            for (j = 0; j < Nb; j++) {

                temp = 0;
                for (m = 0; m < Nb; m++) {
                    temp += (byte) (tempState[i * 4 + m] * colTransMatrix[m][j]);
                }
                state[i * 4 + j] = temp;
            }

        }
    }

    /*********************************************************************************
       *Function:  	standardInvMix_columns
       *Description：  列逆向变换
       *Input:  		byte[] state
       *Output:  	byte[] state
       *Return:  	VOID
       *Others:
     *CreatBy: 	chendz
     *Modify:
     **********************************************************************************/
    private void standardInvMix_columns(byte[] state) {
        int i, j, m;
        byte[] tempState;
        byte temp;
        tempState = Arrays.copyOf(state, state.length);

        for (i = 0; i < 4; i++) {
            for (j = 0; j < Nb; j++) {

                temp = 0;
                for (m = 0; m < Nb; m++) {
                    temp += (byte) (tempState[i * 4 + m] * colInterTransMatrix[m][j]);
                }
                state[i * 4 + j] = temp;
            }

        }
    }

    /*********************************************************************************
       *Function:  	shift_rows
       *Description：  	对数据矩阵进行行变换; ShiftRows是一个置换操作，它将State矩阵中的字节向左旋转
       *Input:  		byte[] state
       *Output:  	byte[] state
       *Return:  	VOID
       *Others:  	State的第0行被向左旋转0个位置，
     *				State的第1行被向左旋转1个位置，
     *         	State的第2行被向左旋转2个位置，
     *         	State的第3行被向左旋转3个位置
     *CreatBy: 	chendz
     *Modify:
     **********************************************************************************/
    private void shift_rows(byte[] state) {
        byte i, k, s, tmp;

        for (i = 1; i < 4; i++) {
            s = 0;
            while (s < i) {
                tmp = state[Nb * i + 0];

                for (k = 1; k < Nb; k++) {
                    state[Nb * i + k - 1] = state[Nb * i + k];
                }

                state[Nb * i + Nb - 1] = tmp;
                s++;
            }
        }
    }

    /*********************************************************************************
       *Function:  	inv_shift_rows
       *Description：  	对数据矩阵进行反向行变换; ShiftRows是一个置换操作，它将State矩阵中的字节向右旋转
       *Input:  		byte[] state
       *Output:  	byte[] state
       *Return:  	VOID
       *Others:  	与函数shift_rows对应
     *CreatBy: 	chendz
     *Modify:
     **********************************************************************************/
    private void inv_shift_rows(byte[] state) {
        byte i, k, s, tmp;

        for (i = 1; i < 4; i++) {
            s = 0;
            while (s < i) {
                tmp = state[Nb * i + Nb - 1];

                for (k = (byte) (Nb - 1); k > 0; k--) {
                    state[Nb * i + k] = state[Nb * i + k - 1];
                }

                state[Nb * i + 0] = tmp;
                s++;
            }
        }
    }

    /*********************************************************************************
       *Function:  	sub_bytes
       *Description：  对数据进行，数值变换
       *Input:  		byte[] state
       *Output:  	byte[] state
       *Return:  	VOID
       *Others:  	将State矩阵中的每个字节替换成一个由Sbox决定的新字节
     比如，State[0,1]的值是0x40如果你想找到它的代替者，你取State[0,1]的值(0x40)并
     让x等于左边的数字(4)并让y等于右边的数字(0)。然后你用x和y作为索引进
     到Sbox表中寻找代替值,Sbox[x,y]
     *CreatBy: 	chendz
     *Modify:
     **********************************************************************************/
    private void sub_bytes(byte[] state) {
        byte i, j;

        for (i = 0; i < 4; i++) {
            for (j = 0; j < Nb; j++) {
                state[Nb * i + j] = (byte) s_box[(state[Nb * i + j] & 0xf0) |
                    (state[Nb * i + j] & 0x0f)];
            }
        }
    }

    /*********************************************************************************
       *Function:  	inv_sub_bytes
       *Description：  对数据进行，数值反向变换
       *Input:  		byte[] state
       *Output:  	byte[] state
       *Return:  	VOID
       *Others:  	与函数sub_bytes相反
     *CreatBy: 	chendz
     *Modify:
     **********************************************************************************/
    private void inv_sub_bytes(byte[] state) {

        byte i, j;

        for (i = 0; i < 4; i++) {
            for (j = 0; j < Nb; j++) {
                state[Nb * i + j] = (byte) inv_s_box[(state[Nb * i + j] & 0xf0) |
                    (state[Nb * i + j] & 0x0f)];
            }
        }
    }

    /*********************************************************************************
       *Function:  	sub_word
       *Description：  对数据进行，数值变换； 用于key值拓展
       *Input:  		byte[] w
       *Output:  	byte[] w
       *Return:  	VOID
       *Others:  
     使用替换表 Sbox 对一个给定的一行密钥调度表 w[] 进行逐字节替换
     比如0x27的代替结果是 x＝2 和 y＝7，并且 Sbox[2,7] 返回 0xcc
     *CreatBy: 	chendz
     *Modify:
     **********************************************************************************/
    private void sub_word(byte[] w) {
        byte i;

        for (i = 0; i < 4; i++) {
            w[i] = (byte) s_box[16 * ((w[i] & 0xf0) >> 4) + (w[i] & 0x0f)];
        }
    }

    /*********************************************************************************
       *Function:  	rot_word
       *Description：  接受一个4个字节的数组并将它们向左旋转一个位置
       *Input:  		byte[] w
       *Output:  	byte[] w
       *Return:  	VOID
       *Others:  
     *CreatBy: 	chendz
     *Modify:
     **********************************************************************************/
    private void rot_word(byte[] w) {

        byte tmp;
        byte i;

        tmp = w[0];

        for (i = 0; i < 3; i++) {
            w[i] = w[i + 1];
        }

        w[3] = tmp;
    }

    /*********************************************************************************
       *Function:  	key_expansion
       *Description：   key值拓展函数
       *Input:  		AesKeyExpand keyExpand
       *Output:  	AesKeyExpand keyExpand
       *Return:  	VOID
       *Others:		AES加密和解密算法使用了一个由种子密钥字节数组生成的密钥调度表。AES规范
     中称之为密钥扩展例程（KeyExpansion）。从本质上讲，从一个原始密钥中生成多
     重密钥以代替使用单个密钥大大增加了比特位的扩散.
     *CreatBy: 	chendz
     *Modify:
     **********************************************************************************/
    private void key_expansion(AesKeyExpand keyExpand) {

        byte[] key = keyExpand.getKeyValue();
        byte[] expand = keyExpand.getKeyExpand();
        int Nk = keyExpand.getNk();
        int Nr = keyExpand.getNr();
        byte tmp[] = new byte[4];
        byte i;
        byte len = (byte) (Nb * (Nr + 1)); // 4*(14+1) = 60, Nk=8,
        // 将种子密钥的值拷贝到密钥调度字节表 w[], 因为 w[] 表总是 4 列，假如种子密钥
        // 是 192 位（24字节），在这种情况下KeyExapansion 将种子密钥拷贝到 w[] 的前面 6 行
        for (i = 0; i < Nk; i++) {
            expand[4 * i + 0] = key[4 * i + 0];
            expand[4 * i + 1] = key[4 * i + 1];
            expand[4 * i + 2] = key[4 * i + 2];
            expand[4 * i + 3] = key[4 * i + 3];
        }

        // 计算剩余w[]的值
        for (i = (byte) Nk; i < len; i++) {
            // w[j-4]
            tmp[0] = expand[4 * (i - 1) + 0];
            tmp[1] = expand[4 * (i - 1) + 1];
            tmp[2] = expand[4 * (i - 1) + 2];
            tmp[3] = expand[4 * (i - 1) + 3];

            if ((i % Nk == 0)) {
                //将tmp循环左移一个字节
                rot_word(tmp);
                // 分别对每个字节按S盒进行映射
                sub_word(tmp);
                // 与32 bits的常量（RC[i/Nk],0,0,0）进行异或
                coef_add(tmp, Rcon((byte) (i / Nk), keyExpand.getRcon()), tmp);
            } else if ((Nk > 6) && ((i % Nk) == 4)) {
                sub_word(tmp);
            }

            expand[4 * i + 0] = (byte) (expand[4 * (i - Nk) + 0] ^ tmp[0]);
            expand[4 * i + 1] = (byte) (expand[4 * (i - Nk) + 1] ^ tmp[1]);
            expand[4 * i + 2] = (byte) (expand[4 * (i - Nk) + 2] ^ tmp[2]);
            expand[4 * i + 3] = (byte) (expand[4 * (i - Nk) + 3] ^ tmp[3]);
        }
    }

    /*********************************************************************************
       *Function:  	addSalt
       *Description：  加盐操作
       *Input:  		byte[] inWithSalt,
     int offset
       *Output:  	byte[] inWithSalt,
       *Return:  	VOID
       *Others:
     *CreatBy: 	chendz
     *Modify:
     **********************************************************************************/
    private void addSalt(byte[] inWithSalt, byte[] saltData) {
        byte i, j;
        for (i = 0; i < 4; i++) {
            for (j = 0; j < Nb; j++) {
                inWithSalt[i + 4 * j] = (byte) (inWithSalt[i + 4 * j] + (saltData[j * Nb + i] - j));
            }
        }
    }

    /*********************************************************************************
       *Function:  	removeSalt
       *Description：  去盐操作
       *Input:  		byte[] inRemoveSalt,
     int offset
       *Output:  	byte[] inRemoveSalt,
       *Return:  	VOID
       *Others:
     *CreatBy: 	chendz
     *Modify:
     **********************************************************************************/
    private void removeSalt(byte[] inRemoveSalt, byte[] saltData) {
        byte i, j;
        for (i = 0; i < 4; i++) {
            for (j = 0; j < Nb; j++) {
                inRemoveSalt[i + 4 * j] = (byte) (inRemoveSalt[i + 4 * j] - (saltData[j * Nb + i] - j));
            }
        }

    }

    /*********************************************************************************
       *Function:  	cipher
       *Description：  加密操作
       *Input:  		byte[] in,
     byte[] w,
     int offsetIn,
     int offsetOut
       *Output:  	byte[] out,
     byte[] in,
       *Return:  	VOID
       *Others:
     *CreatBy: 	chendz
     *Modify:
     **********************************************************************************/
    private void cipher(byte[] in, byte[] out, AesKeyExpand keyExpand, int offsetIn, int offsetOut) {

        byte state[] = new byte[4 * Nb];
        byte r, i, j;
        // 将输入数组拷贝到state(4*4矩阵)
        for (i = 0; i < 4; i++) {
            for (j = 0; j < Nb; j++) {
                state[Nb * i + j] = in[i + 4 * j + offsetIn];
            }
        }

        add_round_key(state, keyExpand.getKeyExpand(), (byte) 0);
        /* 加盐处理  */
        addSalt(state, keyExpand.getSaltData());
        // Nr=14
        for (r = 1; r < keyExpand.getNr(); r++) {
            // 将State矩阵中的每个字节替换成一个由Sbox决定的新字节
            sub_bytes(state);
            // 将State矩阵中的字节向左旋转
            shift_rows(state);
            // 将State字节列的值进行数学"域加"和"域乘"的结果代替每个字节
            standardMix_columns(state);
            /* 加盐处理  */
            addSalt(state, keyExpand.getSaltData());
            // 轮密钥加
            add_round_key(state, keyExpand.getKeyExpand(), r);
        }

        sub_bytes(state);
        shift_rows(state);
        add_round_key(state, keyExpand.getKeyExpand(), (byte) keyExpand.getNr());

        for (i = 0; i < 4; i++) {
            for (j = 0; j < Nb; j++) {
                out[i + 4 * j + offsetOut] = state[Nb * i + j];
            }
        }
    }

    /*********************************************************************************
       *Function:  	inv_cipher
       *Description：  解密操作
       *Input:  		byte[] in,
     byte[] w,
     int offsetIn,
     int offsetOut
       *Output:  	byte[] out,
       *Return:  	VOID
       *Others:
     *CreatBy: 	chendz
     *Modify:
     **********************************************************************************/
    private void inv_cipher(byte[] in, byte[] out, AesKeyExpand keyExpand, int offsetIn, int offsetOut) {

        byte state[] = new byte[4 * Nb];
        byte r, i, j;

        for (i = 0; i < 4; i++) {
            for (j = 0; j < Nb; j++) {
                state[Nb * i + j] = in[i + 4 * j + offsetIn];
            }
        }

        add_round_key(state, keyExpand.getKeyExpand(), (byte) keyExpand.getNr());

        for (r = (byte) (keyExpand.getNr() - 1); r >= 1; r--) {
            inv_shift_rows(state);
            inv_sub_bytes(state);
            add_round_key(state, keyExpand.getKeyExpand(), r);
            removeSalt(state, keyExpand.getSaltData());
            standardInvMix_columns(state);
        }

        inv_shift_rows(state);
        inv_sub_bytes(state);
        removeSalt(state, keyExpand.getSaltData());
        add_round_key(state, keyExpand.getKeyExpand(), (byte) 0);

        for (i = 0; i < 4; i++) {
            for (j = 0; j < Nb; j++) {
                out[i + 4 * j + offsetOut] = state[Nb * i + j];
            }
        }
    }

    /*********************************************************************************
       *Function:  	memcpyInit
       *Description：  初始化数据
       *Input:  		byte[]  srcData,
     int length,
     int desOffset,
     int srcOffset
       *Output:  	byte[] desData,
       *Return:  	VOID
       *Others:
     *CreatBy: 	chendz
     *Modify:
     **********************************************************************************/
    private void memcpyInit(byte[] desData, byte[] srcData, int length, int desOffset, int srcOffset) {
        int i = 0;
        for (; length > 0; length--) {
            desData[desOffset + i] = srcData[srcOffset + i];
            i++;
        }
    }

    /*********************************************************************************
       *Function:  	encodeData
       *Description：  加密批量数据
       *Input:  		byte originalIn[],
     int dataLength
       *Output:  	byte encodeData[],
       *Return:  	VOID
       *Others:
     *CreatBy: 	chendz
     *Modify:
     **********************************************************************************/
    private void encodeData(byte originalIn[], byte encodeData[], int dataLength, AesKeyExpand keyExpand,
                            int headOffset) {
        int encodeLoop;
        int uiLoop;
        byte tailData[];
        byte rawData[];
        int temp;
        rawData = Arrays.copyOf(originalIn, originalIn.length);
        /* 循环次数 */
        encodeLoop = dataLength / 16;

        // 加密
        for (uiLoop = 0; uiLoop < encodeLoop; uiLoop++) {
            temp = uiLoop * 16;
            cipher(rawData /* in */, encodeData /* out */, keyExpand /* expanded key */, temp, temp + headOffset);
        }

        if (0 != (dataLength % 16)) {
            tailData = new byte[16];
            memcpyInit(tailData, rawData, dataLength % 16, 0, uiLoop * 16);
            cipher(tailData /* in */, encodeData /* out */, keyExpand /* expanded key */, 0, uiLoop * 16 + headOffset);
        }

        return;
    }

    /*********************************************************************************
       *Function:  	encodeData
       *Description：  加密批量数据
       *Input:  		byte originalIn[],
     int dataLength
       *Output:  	byte encodeData[],
       *Return:  	VOID
       *Others:
     *CreatBy: 	chendz
     *Modify:
     **********************************************************************************/
    public void encodeData(byte originalIn[], byte encodeData[], int dataLength, AesKeyExpand keyExpand) {
        encodeData(originalIn, encodeData, dataLength, keyExpand, 0);
        return;
    }

    /*********************************************************************************
       *Function:  	decodeData
       *Description: 解密批量数据
       *Input:  		byte encodeData[],
     int dataLength
       *Output:  	byte decodeData[],
       *Return:  	VOID
       *Others:
     *CreatBy: 	chendz
     *Modify:
     **********************************************************************************/
    public boolean decodeData(byte encodeData[], byte decodeData[], int dataLength, AesKeyExpand keyExpand) {
        if (encodeData.length < dataLength) {
            /* 加密后的数据一定大于等于原本的数据长度 否则解密失败。 */
            return false;
        }
        decodeData(encodeData, decodeData, dataLength, keyExpand, 0);
        return true;
    }

    /*********************************************************************************
       *Function:  	decodeData
       *Description: 解密批量数据
       *Input:  		byte encodeData[],
     int dataLength
       *Output:  	byte decodeData[],
       *Return:  	VOID
       *Others:
     *CreatBy: 	chendz
     *Modify:
     **********************************************************************************/
    private void decodeData(byte encodeData[], byte decodeData[], int dataLength, AesKeyExpand keyExpand,
                            int headOffset) {
        int encodeLoop;
        int uiLoop;
        byte[] tailData;
        int temp;

        /* 循环次数 */
        encodeLoop = dataLength / 16;
        // 加密
        for (uiLoop = 0; uiLoop < encodeLoop; uiLoop++) {
            temp = uiLoop * 16;
            inv_cipher(encodeData /* in */, decodeData /* out */, keyExpand /* expanded key */, temp + headOffset,
	            temp);
        }


        if (0 != (dataLength % 16)) {
            tailData = new byte[16];
            inv_cipher(encodeData /* in */, tailData /* out */, keyExpand /* expanded key */,
	            encodeLoop * 16 + headOffset, 0);
            memcpyInit(decodeData, tailData, dataLength % 16, encodeLoop * 16, 0);
        }

        return;
    }

    /*********************************************************************************
       *Function:  	encodeData
       *Description：  加密批量数据
       *Input:  		byte originalIn[],
     int dataLength
       *Output:  	byte encodeData[],
       *Return:  	VOID
       *Others:
     *CreatBy: 	chendz
     *Modify:
     **********************************************************************************/
    private void encodeData(byte originalIn[], byte encodeData[], int dataLength) {
        if (HANDLE_TYPE_RANDOM_KEY_ENCODE == this.handleType) {
            RandomData data = this.setRandomKey();
            String randomKey = this.getRandomKey(data);
            initialKeyAndSaltByStr(randomKey);
        } else if (defaultKeyExpand == null) {
            this.defaultKeyExpand = new AesKeyExpand(defaultKeyValue, defaultSaltData);
            initialExpand(this.defaultKeyExpand);
        }

        encodeData(originalIn, encodeData, dataLength, defaultKeyExpand, 8);
    }

    /*********************************************************************************
       *Function:  	decodeData
       *Description: 解密批量数据
       *Input:  		byte encodeData[],
     int dataLength
       *Output:  	byte decodeData[],
       *Return:  	VOID
       *Others:
     *CreatBy: 	chendz
     *Modify:
     **********************************************************************************/
    private void decodeData(byte encodeData[], byte decodeData[], int dataLength) {
        if (HANDLE_TYPE_RANDOM_KEY_DECODE == this.handleType) {
            String randomKey = this.getRandomKey();
            System.out.println("randomKey-------------------> :"+randomKey);
            initialKeyAndSaltByStr(randomKey);
        } else if (defaultKeyExpand == null) {
            this.defaultKeyExpand = new AesKeyExpand(defaultKeyValue, defaultSaltData);
            initialExpand(this.defaultKeyExpand);
        }

        decodeData(encodeData, decodeData, dataLength, this.defaultKeyExpand, 8);
        return;
    }

    /*********************************************************************************
       *Function:  	getTlvHead
       *Description: 获取tlv消息的头
       *Input:  		int type ,
     int length
       *Output:  	无
       *Return:  	byte[]
       *Others:
     *CreatBy: 	chendz
     *Modify:
     **********************************************************************************/
    private byte[] getTlvHead(int type, int length) {
        byte[] head = new byte[8];
        head[0] = (byte) ((type >> 24) & 0xFF);
        head[1] = (byte) ((type >> 16) & 0xFF);
        head[2] = (byte) ((type >> 8) & 0xFF);
        head[3] = (byte) (type & 0xFF);

        head[4] = (byte) ((length >> 24) & 0xFF);
        head[5] = (byte) ((length >> 16) & 0xFF);
        head[6] = (byte) ((length >> 8) & 0xFF);
        head[7] = (byte) (length & 0xFF);

        return head;
    }

    /*********************************************************************************
       *Function:  	getTypeAndLenthFromHead
       *Description: 获取tlv消息的头
       *Input:  		byte[] head
       *Output:  	无
       *Return:  	int[] 从tlv消息头中获取消息类型int[0]和 长度int[1]。
     其中长度指数据编码前的长度。
       *Others:
     *CreatBy: 	chendz
     *Modify:
     **********************************************************************************/
    private int[] getTypeAndLenthFromHead(byte[] head) {
        int[] typeAndLenth = new int[2];
        /* 获取数据类型 */
        typeAndLenth[0] = (((int) head[0] << 24) & 0xff000000) | (((int) head[1] << 16) & 0xff0000) |
            (((int) head[2] << 8) & 0xff00) | (((int) head[3]) & 0xff);
        /* 获取数据长度 */
        typeAndLenth[1] = (((int) head[4] << 24) & 0xff000000) | (((int) head[5] << 16) & 0xff0000) |
            (((int) head[6] << 8) & 0xff00) | (((int) head[7]) & 0xff);
        return typeAndLenth;
    }

    /*********************************************************************************
       *Function:  	encodeStr
       *Description: 加密String 并进行base64编码
       *Input:  		String originalStr
       *Output:  	无
       *Return:  	String
       *Others:
     *CreatBy: 	chendz
     *Modify:
     **********************************************************************************/
    public String encodeStr(String originalStr) {
        byte orgData[];
        byte enData[];
        byte head[];
        int encodeLen = 0;
        String encodeStr = null;
        if (0 == originalStr.length()) {
            return null;
        }

        orgData = originalStr.getBytes();
        encodeLen = orgData.length / 16;
        if (0 != (orgData.length % 16)) {
            encodeLen++;
        }
        encodeLen *= 16;
        head = getTlvHead(ENCODE_DATA_TYPE, orgData.length);
        enData = new byte[encodeLen + head.length];
        System.arraycopy(head, 0, enData, 0, head.length);
        encodeData(orgData, enData, orgData.length);

        encodeStr = Base64.getEncoder().encodeToString(enData);
        encodeStr = encodeStr.replace('+', '#');
        return encodeStr;
    }

    /*********************************************************************************
       *Function:  	encodeStr
       *Description: 进行base64解码，并解密String
       *Input:  		String originalStr
       *Output:  	无
       *Return:  	String
       *Others:
     *CreatBy: 	chendz
     *Modify:
     **********************************************************************************/
    public String decodeStr(String encodeStr) {
        byte orgData[];
        byte enData[];
        int typeLenth[];
        String decodeStr = null;
        if (0 == encodeStr.length()) {
            return null;
        }

        encodeStr = encodeStr.replace('#', '+');
        enData = Base64.getDecoder().decode(encodeStr);
        typeLenth = getTypeAndLenthFromHead(enData);

        if (ENCODE_DATA_TYPE != typeLenth[0]) {
            return null;
        }
        if ((enData.length - 8) < typeLenth[1]) {
            /* 加密后的数据一定大于等于原本的数据长度 否则解密失败。 */
            return null;
        }
        orgData = new byte[typeLenth[1]];
        decodeData(enData, orgData, typeLenth[1]);
        decodeStr = new String(orgData);

        return decodeStr;
    }

    /*********************************************************************************
       *Function:    printBytes
       *Description: 调试用函数，发布的时候请删除
       *Input:       byte data[],
     String info
       *Output:      无
       *Return:      void
       *Others:
     *CreatBy:     chendz
     *Modify:
     **********************************************************************************/
    protected void printBytes(byte data[], String info) {
        int i;
        System.out.println(info + ": ");
        for (i = 0; i < data.length; i++) {
            if (0 == (data[i] & 0xf0)) {
                System.out.printf("0");
            }
            System.out.printf("%x, ", data[i]);
        }
        System.out.printf("\n");

    }

    /*********************************************************************************
       *Function:    getRandomKey
       *Description: 获取随机的key值 -- 从cookie中获取
       *Input:       无
       *Output:      无
       *Return:      String
       *Others:
     *CreatBy:     chendz
     *Modify:
     **********************************************************************************/
    private String getRandomKey() {
        return getRandomKey(this.getRandomDataForMeeting());
    }

    /*********************************************************************************
       *Function:    transCntValue
       *Description: 对计数进行变换 并转换为字符串。
       *Input:       cnt
       *Output:      无
       *Return:      String
       *Others:
     *CreatBy:     chendz
     *Modify:
     **********************************************************************************/
    private String transCntValue(int cnt) {
        /* 变化A */
        int col = 3 - (cnt % 4);
        int row = (cnt / 4) % 4;
        byte transA = colInterTransMatrix[row][col];
        /* 变化B */
        byte transB = defaultKeyValue[((cnt % 32) * (cnt / 32)) % 32];
        /* 变换C */
        byte transC = defaultSaltData[((cnt % 32) + (cnt / 32)) % 16];
        /* 变换D */
        byte transD = (byte) s_box[((cnt % 32) + (cnt / 32)) % 256];
        /* 变换E */
        byte transE = (byte) inv_s_box[((cnt % 32) + (cnt / 32)) % 256];

        /* 最终变换 */
        int result = ((((int) transA << 8) & 0xff00) | ((int) transB & 0xff)) ^
            (((int) transC & 0xff) ^ ((int) transD & 0xff)) ^
            ((transE << 8) & 0xff00);
        return (result + "");
    }

    /*********************************************************************************
       *Function:    transStrValue
       *Description: 对字符串进行变换 。
       *Input:       cnt
       *Output:      无
       *Return:      String
       *Others:
     *CreatBy:     chendz
     *Modify:
     **********************************************************************************/
    private String transStrValue(String strVal) {
        int iCnt;
        int iNext;
        String resultStr;
        byte[] data = strVal.getBytes();
        byte[] transBytes = new byte[data.length];
        byte[] usedData = new byte[data.length];

        /* 初始化 */
        for (iCnt = 0; iCnt < data.length; iCnt++) {
            transBytes[iCnt] = 0;
            usedData[iCnt] = 0;
        }
        /* 变换 */
        for (iCnt = 0; iCnt < data.length; iCnt++) {
            byte tempData = data[iCnt];
            iNext = tempData % data.length;
            while (usedData[iNext] != 0) {
                if ((iNext % 2) == 0) {
                    tempData += data[iNext];
                } else {
                    tempData += transBytes[iNext];
                }
                iNext++;
                iNext = iNext % data.length;
            }
            transBytes[iNext] = tempData;
            usedData[iNext] = 1;
        }

        resultStr = Base64.getEncoder().encodeToString(transBytes);
        resultStr = resultStr.replace('+', '#');
        return resultStr;
    }

    /*********************************************************************************
       *Function:    getRandomKey
       *Description: 获取随机的key值  -- 从传入的值中获取
       *Input:       无
       *Output:      无
       *Return:      String
       *Others:
     *CreatBy:     chendz
     *Modify:
     **********************************************************************************/
    private String getRandomKey(RandomData randomData) {

        /***----------变换--------------***/
        String resultStr;
        randomData.setCnt(1);
        System.out.println("randomData--------------------------->: "+randomData);
        System.out.println("cnt--------------------------->: "+randomData.getCnt());
        resultStr = transCntValue(randomData.getCnt());
        resultStr = resultStr + randomData.getSessionId() + randomData.getCnt() +
            randomData.getTimestamp() + randomData.getUserId() + randomData.getRandom();
        System.out.println("resultStr--------------------------->: "+resultStr);
        resultStr = transStrValue(resultStr);

        return resultStr;
    }

    /*********************************************************************************
       *Function:    setRandomKey
       *Description: 设置随机的key值
       *Input:       无
       *Output:      无
       *Return:      String
       *Others:
     *CreatBy:     chendz
     *Modify:
     **********************************************************************************/
    private RandomData setRandomKey() {
        RandomData value;
        value = this.getRandomData();
        if (null == value) {
            return null;
        }
        value.setCnt(value.getCnt() + 1);
        SimpleDateFormat format = new SimpleDateFormat("yyyyMMddhhmmss");
        String timestamp = format.format(new Date());

        value.setTimestamp(timestamp);
        this.setRandomData(value);

        return value;
    }

    /*********************************************************************************
       *Function:    getRandomData
       *Description: 获取cookie内存的值
       *Input:       无
       *Output:      无
       *Return:      RandomData randomData
       *Others:
     *CreatBy:     chendz
     *Modify:
     **********************************************************************************/
    private RandomData getRandomData() {
        return aesCipherUtil.getRandomData();
    }

    private RandomData getRandomDataForMeeting() {
        return aesCipherUtil.getRandomDataForMeeting();
    }

    /*********************************************************************************
       *Function:    setRandomData
       *Description: 设置cookie内存的值
       *Input:       RandomData randomData
       *Output:      无
       *Return:      void
       *Others:
     *CreatBy:     chendz
     *Modify:
     **********************************************************************************/
    private void setRandomData(RandomData randomData) {
        aesCipherUtil.saveRandomData(randomData);
        return;
    }

}
